#include <ros/ros.h>
#include "actionlib/client/simple_action_client.h"
#include "actionlib_test/AddIntsAction.h"
/*  simple_action_client.h的API：https://docs.ros.org/en/api/actionlib/html/classactionlib_1_1SimpleActionClient.html
	simple_action_server.h的API：https://docs.ros.org/en/api/actionlib/html/classactionlib_1_1SimpleActionServer.html
    流程:		1.包含头文件;				2.初始化ROS节点;							3.创建NodeHandle;
        		4.创建action客户端对象;		5.发送目标，处理反馈以及最终结果;         6.spin().
*/

// 在ROS的actionlib框架中，任务状态通常会经历一系列预定义的状态变化。这里的`StateEnum`枚举类型列举了动作执行过程中可能的状态：
// 1. **PENDING**: 表示动作目标已经被接收但还没有开始执行，或者正在等待资源以便能够开始执行。
// 2. **ACTIVE**: 指目标已经开始被动作服务器处理或执行中。
// 3. **RECALLED**: 在动作目标开始执行前收到了取消请求，并且成功地被召回（取消请求并停止执行流程）。
// 4. **REJECTED**: 动作目标被操作服务器拒绝执行，通常是因为目标无效、无法实现或系统不支持。
// 5. **PREEMPTED**: 在动作目标开始执行后收到取消请求，并且服务器已经完成执行过程中的清理工作并提前终止了目标的执行。
// 6. **ABORTED**: 示动作目标由于某些错误或异常而被迫中断执行，并没有成功完成任务。
// 7. **SUCCEEDED**: 表示动作目标已由动作服务器成功完成，并达到了预期的结果。
// 8. **LOST** (有时候这个状态并不一定会出现在所有actionlib实现中): 一种较为特殊的状态，它表示动作服务器失去了与目标相关的某些重要信息，导致目标状态无法确定或丢失了跟踪。
// 这些状态是长时运行的动作任务生命周期管理中的关键部分，有助于开发者和用户监控和理解动作任务的执行进展及结果。

// 在ROS的actionlib框架中，setPreempted()方法是用来将Action服务器上的目标状态设置为PREEMPTED，而不是REJECTED。
// 当一个Action客户端请求取消（preempt）一个正在进行的操作时，服务器应该调用setPreempted()来报告给客户端该目标已经被预取消，并且可能已经提前终止执行。

typedef actionlib::SimpleActionClient<actionlib_test::AddIntsAction> Client;
static Client *client_ptr;

void done_cb(const actionlib::SimpleClientGoalState &state, const actionlib_test::AddIntsResultConstPtr &result){
    if (state.state_ == state.SUCCEEDED){
        ROS_INFO("最终结果:%d",result->result); 
    }else if(state.state_ == state.PREEMPTED){
        ROS_INFO("计算被canceled, 最终结果:%d",result->result); 
    }else{
        ROS_INFO("任务失败！"); 
    }
}
void active_cb(){ ROS_INFO("服务已经被激活...."); }
void feedback_cb(const actionlib_test::AddIntsFeedbackConstPtr &feedback){
    ROS_INFO("当前进度:%.2f",feedback->progress_bar);
    // 把向下面的注释打开会有提前取消的动作
    // 在client里面，在30%进度的时候，调用cancelGoal()取消操作
    // if (feedback->progress_bar == 0.30)
	// 	client_ptr->cancelGoal();
}

// client取消后，server那边必须要响应才行，不然取消不了。
// client调用cancelGoal()时，server端这边isPreemptRequested()会变为true，
// 这个时候setPreempted()确认一下，函数直接退出就可以了，此时action的state状态由ACTIVE转变为PREEMPTED。
// 取消的时候，server这边顺便可以把结果（result）带上去。

int main(int argc, char *argv[]){
    setlocale(LC_ALL,"");
    ros::init(argc,argv,"AddInts_client");
    ros::NodeHandle nh;						// 3.创建NodeHandle;
    Client client(nh,"addInts",true);		// 4.创建action客户端对象;   "addInts"：The action name
    client_ptr = &client;
    ROS_INFO("等待服务启动");
    client.waitForServer();					// 等待服务启动
    ROS_INFO("已经启动，发送目标点，如果不启动服务，会走到这里么");
    actionlib_test::AddIntsGoal goal;		// 5.发送目标，处理反馈以及最终结果;
    goal.num = 100;
    client.sendGoal(goal,&done_cb,&active_cb,&feedback_cb);
    ros::spin();
    return 0;
}

